package org.linlinjava.litemall.admin.service;

import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.linlinjava.litemall.admin.vo.AdminStrategyCouponStatisticVO;
import org.linlinjava.litemall.admin.vo.AdminStrategyGeneralVO;
import org.linlinjava.litemall.admin.vo.coupon.CouponStatisticDetailReqVO;
import org.linlinjava.litemall.admin.vo.coupon.CouponStatisticDetailRespVO;
import org.linlinjava.litemall.admin.vo.coupon.CouponStatisticReqVO;
import org.linlinjava.litemall.core.util.BigDecimalUtil;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.linlinjava.litemall.db.domain.*;
import org.linlinjava.litemall.db.service.*;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class AdminLumiereCouponUserService {

    private final LumiereCouponUserService lumiereCouponUserService;
    private final LumiereCouponStrategyService lumiereCouponStrategyService;
    private final LitemallOrderService litemallOrderService;
    private final LumiereCouponService lumiereCouponService;
    private final LitemallUserService litemallUserService;
    private final LitemallShopService litemallShopService;

    public ResponseUtil.Response general() {

        List<LumiereCouponStrategy> couponStrategyList = lumiereCouponStrategyService.list();
        if(CollUtil.isEmpty(couponStrategyList)){
            return ResponseUtil.ok(AdminStrategyGeneralVO.builder().build());
        }
        List<Long> couponStrategyIdsList = CollStreamUtil.toList(couponStrategyList,LumiereCouponStrategy::getId);

        // 优惠券用户总数
        Long lumiereCouponUserCount = Optional.ofNullable(
                lumiereCouponUserService.count(Wrappers.lambdaQuery(LumiereCouponUser.class)
                        .in(LumiereCouponUser::getStrategyId,couponStrategyIdsList))
        ).orElse(0L);

        // 发放数量
        Long pullNumber = lumiereCouponStrategyService.pullSum();

        // 领取数量等于优惠券用户总数
        Long receivingNumber = lumiereCouponUserCount;

        // 已使用
        long useCount = lumiereCouponUserService.count(Wrappers.lambdaQuery(LumiereCouponUser.class)
                .in(LumiereCouponUser::getStrategyId,couponStrategyIdsList)
                .isNotNull(LumiereCouponUser::getUsedTime));

        // 已过期
        long expireNum = lumiereCouponUserService.count(Wrappers.lambdaQuery(LumiereCouponUser.class)
                .in(LumiereCouponUser::getStrategyId,couponStrategyIdsList)
                .isNull(LumiereCouponUser::getUsedTime)
                .lt(LumiereCouponUser::getEndTime, LocalDateTime.now()));

        // 获取所有已使用的优惠券用户（绑定了订单的）
        List<LumiereCouponUser> usedCouponUsers = lumiereCouponUserService.list(Wrappers.lambdaQuery(LumiereCouponUser.class)
                .in(LumiereCouponUser::getStrategyId,couponStrategyIdsList)
                .isNotNull(LumiereCouponUser::getOrderId));

        // 批量提取订单ID并转成 Integer 类型
        List<Integer> orderIds = usedCouponUsers.stream()
                .map(user -> user.getOrderId() == null ? null : user.getOrderId().intValue())
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        // 批量查询订单（避免 N+1 查询）
        List<LitemallOrder> orderList = orderIds.isEmpty() ? Collections.emptyList() : litemallOrderService.findByIds(orderIds);

        // 累加实际价格和优惠金额
        BigDecimal orderTotalActual = BigDecimalUtil.sum(orderList,order -> Optional.ofNullable(order.getActualPrice()).orElse(BigDecimal.ZERO));

        BigDecimal orderTotalDiscount = BigDecimalUtil.sum(orderList,order -> Optional.ofNullable(order.getCouponPrice()).orElse(BigDecimal.ZERO));

        return ResponseUtil.ok(AdminStrategyGeneralVO.builder()
                .pullNumber(pullNumber)
                .receivingNumber(receivingNumber)
                .useCount(useCount)
                .expireNum(expireNum)
                .orderTotalActual(orderTotalActual)
                .orderTotalDiscount(orderTotalDiscount)
                .build());
    }


    public ResponseUtil.Response strategyCouponStatistic(CouponStatisticReqVO reqVO) {

        List<LumiereCoupon> couponList = lumiereCouponService.list(Wrappers.lambdaQuery(LumiereCoupon.class)
                .like(StrUtil.isNotBlank(reqVO.getCouponName()), LumiereCoupon::getName, reqVO.getCouponName()));

        List<Long> couponIds = couponList.stream().map(LumiereCoupon::getId).collect(Collectors.toList());

        IPage<LumiereCouponStrategy> strategyList = lumiereCouponStrategyService.page(
                new Page<>(reqVO.getPage(), reqVO.getLimit()),
                Wrappers.lambdaQuery(LumiereCouponStrategy.class)
                        .in(CollUtil.isNotEmpty(couponIds), LumiereCouponStrategy::getCouponId, couponIds)
                        .eq(reqVO.getDiscountType() != null, LumiereCouponStrategy::getDiscountType, reqVO.getDiscountType())
                        .like(reqVO.getRangesType() != null, LumiereCouponStrategy::getRegions, reqVO.getRangesType())
                        .eq(StrUtil.isNotBlank(reqVO.getStrategyName()), LumiereCouponStrategy::getName, reqVO.getStrategyName())
                        .eq(reqVO.getCouponType() != null, LumiereCouponStrategy::getCouponType, reqVO.getCouponType())
        );

        List<LumiereCouponStrategy> strategyRecords = strategyList.getRecords();
        if (CollUtil.isEmpty(strategyRecords)) {
            return ResponseUtil.okPage(strategyList);
        }

        // 批量获取相关coupon和couponUser、order信息
        Set<Long> strategyCouponIds = strategyRecords.stream().map(LumiereCouponStrategy::getCouponId).collect(Collectors.toSet());

        // 1. 获取 couponId -> name 映射
        Map<Long, String> couponIdNameMap = CollStreamUtil.toMap( lumiereCouponService.listByIds(strategyCouponIds),LumiereCoupon::getId, LumiereCoupon::getName);

        // 2. 获取所有关联的用户
        List<LumiereCouponUser> allUsers = lumiereCouponUserService.list(
                Wrappers.lambdaQuery(LumiereCouponUser.class)
                        .in(LumiereCouponUser::getCouponId, strategyCouponIds)
        );

        // 3. 用户按 couponId、strategyId 分组
        Map<Long, List<LumiereCouponUser>> usersByCouponId = CollStreamUtil.groupByKey(allUsers,LumiereCouponUser::getCouponId);
        Map<Long, List<LumiereCouponUser>> usersByStrategyId = CollStreamUtil.groupByKey(allUsers,LumiereCouponUser::getStrategyId);

        // 4. 筛选出有订单的用户
        List<LumiereCouponUser> usedUsers = allUsers.stream()
                .filter(user -> user.getOrderId() != null)
                .collect(Collectors.toList());

        List<Integer> orderIds = usedUsers.stream()
                .map(LumiereCouponUser::getOrderId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        Map<Integer, LitemallOrder> orderMap = orderIds.isEmpty() ? Collections.emptyMap() :
                litemallOrderService.findByIds(orderIds).stream()
                        .collect(Collectors.toMap(LitemallOrder::getId, o -> o));

        // 组装统计结果
        List<AdminStrategyCouponStatisticVO> result = new ArrayList<>();
        for (LumiereCouponStrategy strategy : strategyRecords) {
            AdminStrategyCouponStatisticVO vo = AdminStrategyCouponStatisticVO.builder().build();
            String strategyName = strategy.getName();
            String couponName = couponIdNameMap.get(strategy.getCouponId());

            List<LumiereCouponUser> users = usersByCouponId.getOrDefault(strategy.getCouponId(), Collections.emptyList());
            List<LumiereCouponUser> strategyUsers = usersByStrategyId.getOrDefault(strategy.getId(), Collections.emptyList());

            long total = strategy.getTotal();
            long received = users.size();
            long used = users.stream().filter(user -> user.getUsedTime() != null).count();
            long expired = users.stream().filter(user -> user.getUsedTime() == null && user.getEndTime().isBefore(LocalDateTime.now())).count();
            long memberUsed = strategyUsers.stream().filter(user -> user.getOrderId() != null).count();

            BigDecimal totalActual = BigDecimal.ZERO;
            BigDecimal totalDiscount = BigDecimal.ZERO;

            for (LumiereCouponUser user : strategyUsers) {
                if (user.getOrderId() != null && orderMap.containsKey(user.getOrderId())) {
                    LitemallOrder order = orderMap.get(user.getOrderId());
                    totalActual = totalActual.add(order.getActualPrice());
                    totalDiscount = totalDiscount.add(order.getCouponPrice());
                }
            }

            Locale locale = Locale.CANADA;
            NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(locale);

            vo.setId(strategy.getId());
            vo.setCouponType(strategy.getCouponType());
            vo.setStrategyName(strategyName);
            vo.setCouponName(couponName);
            vo.setPullNumber(total);
            vo.setReceivingNumber(received);
            vo.setUseNum(used);
            vo.setExpireNum(expired);
            vo.setMemberUseNum(memberUsed);
            vo.setOrderTotalActual(totalActual.doubleValue() == 0.00 ? "$0" : currencyFormat.format(totalActual));
            vo.setOrderTotalDiscount(totalDiscount.doubleValue() == 0.00 ? "$0" : currencyFormat.format(totalDiscount));
            vo.setRanges(strategy.getRanges());
            vo.setDiscountType(strategy.getDiscountType());
            vo.setTimeType(strategy.getTimeType());
            vo.setDays(strategy.getDays());
            vo.setStartTime(strategy.getStartTime());
            vo.setEndTime(strategy.getEndTime());

            result.add(vo);
        }
        return ResponseUtil.okPage(strategyList,result);
    }


    public ResponseUtil.Response strategyCouponStatisticDetail(CouponStatisticDetailReqVO reqVO) {
        List<LumiereCoupon> couponList = lumiereCouponService.list(Wrappers.lambdaQuery(LumiereCoupon.class)
                .like(StrUtil.isNotBlank(reqVO.getCouponName()), LumiereCoupon::getName, reqVO.getCouponName()));

        List<LitemallUser> userList = litemallUserService.list(Wrappers.lambdaQuery(LitemallUser.class).like(StrUtil.isNotBlank(reqVO.getUserName()), LitemallUser::getUsername, reqVO.getUserName()));

        List couponStatisticDetailRespVOList = new ArrayList<>();

        IPage<LumiereCouponUser> strategyList = lumiereCouponUserService.page(new Page<>(reqVO.getPage(),reqVO.getLimit()),Wrappers.lambdaQuery(LumiereCouponUser.class)
                .eq( LumiereCouponUser::getStrategyId, reqVO.getStrategyId())
                .between(reqVO.getStartTime() != null && reqVO.getEndTime() != null, LumiereCouponUser::getAddTime, reqVO.getStartTime(), reqVO.getEndTime())
                .in(couponList.size() > 0, LumiereCouponUser::getCouponId, CollStreamUtil.toList(couponList, LumiereCoupon::getId))
                .eq(reqVO.getShopId()!=null, LumiereCouponUser::getShopId, reqVO.getShopId())
                .in(userList.size()>0,LumiereCouponUser::getUserId, CollStreamUtil.toList(userList, LitemallUser::getId)));
        strategyList.getRecords().stream().forEach(couponUser ->{
            LumiereCouponStrategy couponStrategy = lumiereCouponStrategyService.getById(couponUser.getStrategyId());
            LumiereCoupon lumiereCoupon = lumiereCouponService.getById(couponUser.getCouponId());

            CouponStatisticDetailRespVO couponStatisticDetailRespVO = CouponStatisticDetailRespVO.builder()
                    .id(couponUser.getId())
                    .strategyName(couponStrategy.getName())
                    .couponName(lumiereCoupon.getName())
                    .shopName(couponUser.getShopId()!=null?litemallShopService.findById(couponUser.getShopId()).getName():null)
                    .userName(litemallUserService.findById(couponUser.getUserId()).getNickname())
                    .addTime(couponUser.getAddTime())
                    .useTime(couponUser.getUsedTime())
                    .startTime(couponUser.getStartTime())
                    .endTime(couponUser.getEndTime())
                    .build();
            if(couponUser.getOrderId() != null){
                LitemallOrder order = litemallOrderService.findById(couponUser.getOrderId());
                if(order != null){
                    couponStatisticDetailRespVO.setOrderTotalActual(order.getActualPrice());
                    couponStatisticDetailRespVO.setOrderTotalDiscount(order.getCouponPrice());
                }
            }
            couponStatisticDetailRespVOList.add(couponStatisticDetailRespVO);
        });
        strategyList.setRecords(couponStatisticDetailRespVOList);
        return ResponseUtil.okPage(strategyList);
    }



}
